﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using MvcCms.Data;
using MvcCms.Data.Pagination;
using MvcCms.Service;
using MvcCms.Service.Cache;
using MvcCms.Service.Code;
using MvcCms.Service.Code.DataTables;
using MvcCms.Service.Code.Email;
using MvcCms.Service.Logging;
using MvcCms.Service.Models;
using MvcCmsOptIn.Data;
using MvcCmsOptIn.Data.Entities;

namespace MvcCmsOptIn.Service
{
    public class OptInService : IOptInService
    {
        #region Constructors

        private readonly IOptInRepository _repository;
        private readonly ICacheService _cacheService;
        public OptInService(IOptInRepository repository, ICacheService cacheService)
        {
            _repository = repository;
            _cacheService = cacheService;
        }

        #endregion

        #region Validation Methods


        #endregion

        #region Subscriber Methods

        public bool ImportSubscribersFromPortal(bool useHTML, bool confirmed, string ipAddress, IEnumerable<OptInList> listsToAddTo, string roles)
        {
            try
            {
                var portalId = _cacheService.GetCurrentPortal().PortalID;
                var accountService = ServiceLocator.Current.GetInstance<IAccountService>();

                var usersToImport = accountService.ListUsersWithProfile();
                if (!string.IsNullOrEmpty(roles))
                {
                    var rolesArray = roles.Split(',');
                    foreach (var role in rolesArray)
                    {
                        var usersInRoleIds = accountService.ListUsersInRole(role).Select(u=>u.UserID);
                        var usersInRoleToAdd =  from user in usersToImport
                                                where usersInRoleIds.Contains(user.UserID)
                                                select user;
                        usersToImport = usersInRoleToAdd;
                    }
                }

                var currentSubscribers = ListSubscribers().Select(s => s.UserID);
                //when things get bigger this would best be done in a sproc
                var usersToAdd = from user in usersToImport
                                 where !currentSubscribers.Contains(user.UserID)
                                 select user;
                usersToAdd = usersToAdd.ToList();
                foreach (var user in usersToAdd)
                {
                    var subscriberExists = _repository.GetSubscriber(user.Email);
                    if (subscriberExists == null)
                    {
                        var doNotContact = false;
                        var format = "HTML";
                        var name = user.UserName;
                        if (user.Profile != null)
                        {
                            format = user.Profile.Subscription;
                            if (user.Profile.Subscription == "None")
                            {
                                doNotContact = true;
                            }
                            if (!string.IsNullOrEmpty(user.Profile.FirstName) || !string.IsNullOrEmpty(user.Profile.LastName))
                            {
                                name = user.Profile.FirstName + " " + user.Profile.LastName;
                            }
                        }
                        var newSubscriber = new OptInSubscriber
                        {
                            Name = name,
                            EMail = user.Email,
                            PortalID = portalId,
                            DoNotContact = doNotContact,
                            Format = format,
                            UserID = user.UserID,
                            CreateDate=DateTime.Now
                        };
                        foreach (var list in listsToAddTo)
                        {
                            var subscriberList = new OptInSubscriberList
                            {
                                ListID = list.ListID,
                                Confirmed = confirmed,
                                HTMLFormat = useHTML,
                                IPAddress = ipAddress.TryTrim(),
                                SubscriptionDate = DateTime.Now,
                                Subscriber = newSubscriber
                            };
                        }
                        _repository.CreateSubscriber(newSubscriber);
                    }
                    else
                    {
                        foreach (var list in listsToAddTo)
                        {
                            var subscriberList = new OptInSubscriberList
                            {
                                ListID = list.ListID,
                                Confirmed = confirmed,
                                HTMLFormat = useHTML,
                                IPAddress = ipAddress.TryTrim(),
                                SubscriptionDate = DateTime.Now,
                                Subscriber = subscriberExists
                            };
                        }
                        _repository.EditSubscriber(subscriberExists);
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
        }

        public bool ImportSubscribersFromCSV(bool useHTML, bool confirmed, string ipAddress, IEnumerable<OptInList> listsToAddTo, IEnumerable<OptInSubscriber> subscribersToAdd, bool doNotAddExisting)
        {
            try
            {
                var portalId = _cacheService.GetCurrentPortal().PortalID;

                foreach (var subscriber in subscribersToAdd)
                {
                    var subscriberExists = _repository.GetSubscriber(subscriber.EMail);
                    if (subscriberExists == null)
                    {
                        subscriber.PortalID = portalId;
                        subscriber.DoNotContact = false;
                        subscriber.Format = "HTML";
                        subscriber.CreateDate = DateTime.Now;

                        foreach (var list in listsToAddTo)
                        {
                            var subscriberList = new OptInSubscriberList
                            {
                                ListID = list.ListID,
                                Confirmed = confirmed,
                                HTMLFormat = useHTML,
                                IPAddress = ipAddress.TryTrim(),
                                SubscriptionDate = DateTime.Now,
                                Subscriber = subscriber
                            };
                        }
                        _repository.CreateSubscriber(subscriber);
                    }
                    else
                    {
                        if (!doNotAddExisting){
                            foreach (var list in listsToAddTo)
                            {
                                var subscriptionExists = subscriberExists.SubscriberLists.Where(l => l.ListID == list.ListID);
                                if (subscriptionExists == null)
                                {
                                    var subscriberList = new OptInSubscriberList
                                    {
                                        ListID = list.ListID,
                                        Confirmed = confirmed,
                                        HTMLFormat = useHTML,
                                        IPAddress = ipAddress.TryTrim(),
                                        SubscriptionDate = DateTime.Now,
                                        Subscriber = subscriberExists
                                    };
                                }
                                _repository.EditSubscriber(subscriberExists);
                            }
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
        }

        public IEnumerable<OptInSubscriber> ListSubscribers()
        {
            return _repository.ListSubscribers(_cacheService.GetCurrentPortal().PortalID);
        }

        public DtoOptInSubscribers ListSubscribers(DataTablesRequest dataTablesRequest, string columnsToReturn)
        {
            var columnNames = columnsToReturn.Split(',');
            var dtoLists = new DtoOptInSubscribers();

            var query = _repository.ListSubscribers(_cacheService.GetCurrentPortal().PortalID).AsQueryable();
            //Get the totalItems/TotalRecords now we need this before filters are applied
            dtoLists.GetPagination.iTotalRecords = query.Count();

            query = query.SearchForDataTables(dataTablesRequest, columnNames) as IQueryable<OptInSubscriber>;
            //Get the totalDisplayRecords after filtering has been applied.
            dtoLists.GetPagination.iTotalDisplayRecords = query.Count();

            query = DataTablesSort.Sort(dataTablesRequest, query, columnNames) as IQueryable<OptInSubscriber>;

            //Pass the number of records after filtering to AsPagination so it doesn't query again for the total
            //The filtered count is the one it is expecting to calculate TotalPages
            var queryPaginated = query.AsPagination(dataTablesRequest.iCurrentPage, dataTablesRequest.iDisplayLength, dtoLists.GetPagination.iTotalDisplayRecords);

            var lists = queryPaginated.ToList();
            var records = from s in lists
                          select new DtoOptInSubscriber
                          {
                              SubscriberID = s.SubscriberID,
                              Name = s.Name,
                              Email = s.EMail,
                              Format = s.Format,
                              DoNotContact = s.DoNotContact
                          };
            dtoLists.AddRange(records);
            return dtoLists;
        }

        public DtoOptInSubscribers ListSubscribers(DataTablesRequest dataTablesRequest, string columnsToReturn, int listId)
        {
            var columnNames = columnsToReturn.Split(',');
            var dtoLists = new DtoOptInSubscribers();

            var query = _repository.ListListSubscribers(listId).AsQueryable();
            //Get the totalItems/TotalRecords now we need this before filters are applied
            dtoLists.GetPagination.iTotalRecords = query.Count();

            query = query.SearchForDataTables(dataTablesRequest, columnNames) as IQueryable<DTOSubscriber>;
            //Get the totalDisplayRecords after filtering has been applied.
            dtoLists.GetPagination.iTotalDisplayRecords = query.Count();

            query = DataTablesSort.Sort(dataTablesRequest, query, columnNames) as IQueryable<DTOSubscriber>;

            //Pass the number of records after filtering to AsPagination so it doesn't query again for the total
            //The filtered count is the one it is expecting to calculate TotalPages
            var queryPaginated = query.AsPagination(dataTablesRequest.iCurrentPage, dataTablesRequest.iDisplayLength, dtoLists.GetPagination.iTotalDisplayRecords);

            var lists = queryPaginated.ToList();
            var records = from s in lists
                          select new DtoOptInSubscriber
                          {
                              SubscriberID = s.SubscriberID,
                              Name = s.Name,
                              Email = s.EMail,
                              Format = s.Format,
                              DoNotContact = s.DoNotContact
                          };
            dtoLists.AddRange(records);
            return dtoLists;
        }

        public IEnumerable<OptInSubscriberList> ListListsSubscribers(int newsLetterID)
        {
            return _repository.ListListsSubscribers(newsLetterID);
        }

        public bool DeleteSubscriber(int subscriberId)
        {
            try
            {
                _repository.DeleteSubscriber(subscriberId);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool DeleteSubscriberList(int subscriberListId)
        {
            try
            {
                _repository.DeleteSubscriberList(subscriberListId);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool EditSubscriber(OptInSubscriber subscriberToEdit)
        {
            // Database logic
            try
            {
                _repository.EditSubscriber(subscriberToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public OptInSubscriber GetSubscriber(int subscriberId)
        {
            return _repository.GetSubscriber(subscriberId);
        }

        public OptInSubscriber GetSubscriber(string email)
        {
            return _repository.GetSubscriber(email);
        }

        public bool CreateSubscriber(OptInSubscriber subscriberToCreate)
        {
            subscriberToCreate.PortalID = _cacheService.GetCurrentPortal().PortalID;
            try
            {
                subscriberToCreate.CreateDate = DateTime.Now;
                _repository.CreateSubscriber(subscriberToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool AddListToSubscriber(int subscriberId, int listId, bool confirmed, bool useHTML, string ipAddress)
        {
            try
            {
                var subscriber = GetSubscriber(subscriberId);
                var list = GetList(listId);

                var subscriberList = new OptInSubscriberList
                {
                    List = list,
                    Confirmed = confirmed,
                    HTMLFormat = useHTML,
                    IPAddress = ipAddress.TryTrim(),
                    SubscriptionDate = DateTime.Now,
                    Subscriber = subscriber
                };
                return EditSubscriber(subscriber);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
        }
        public bool RemoveListFromSubscriber(int subscriberId, int listId)
        {
            try
            {
                var subscriber = GetSubscriber(subscriberId);
                var subscriberListToRemove = subscriber.SubscriberLists.Where(sl => sl.ListID == listId).FirstOrDefault();
                DeleteSubscriberList(subscriberListToRemove.SubscriberListID);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }
        #endregion

        #region List Methods

        public bool DeleteList(int listId)
        {
            try
            {
                _repository.DeleteList(listId);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool EditList(OptInList listToEdit)
        {
            // Database logic
            try
            {
                _repository.EditList(listToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public OptInList GetList(int listId)
        {
            return _repository.GetList(listId);
        }

        public bool CreateList(OptInList listToCreate)
        {
            listToCreate.PortalID = _cacheService.GetCurrentPortal().PortalID;
            try
            {
                _repository.CreateList(listToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public IEnumerable<OptInList> ListLists()
        {
            return _repository.ListLists(_cacheService.GetCurrentPortal().PortalID);
        }

        public DtoOptInLists ListLists(DataTablesRequest dataTablesRequest, string columnsToReturn)
        {
            var columnNames = columnsToReturn.Split(',');
            var dtoLists = new DtoOptInLists();

            var query = _repository.ListListsWithCounts(_cacheService.GetCurrentPortal().PortalID).AsQueryable();
            //Get the totalItems/TotalRecords now we need this before filters are applied
            dtoLists.GetPagination.iTotalRecords = query.Count();

            query = query.SearchForDataTables(dataTablesRequest, columnNames) as IQueryable<OptInListWithCounts>;
            //Get the totalDisplayRecords after filtering has been applied.
            dtoLists.GetPagination.iTotalDisplayRecords = query.Count();

            query = DataTablesSort.Sort(dataTablesRequest, query, columnNames) as IQueryable<OptInListWithCounts>;

            //Pass the number of records after filtering to AsPagination so it doesn't query again for the total
            //The filtered count is the one it is expecting to calculate TotalPages
            var queryPaginated = query.AsPagination(dataTablesRequest.iCurrentPage, dataTablesRequest.iDisplayLength, dtoLists.GetPagination.iTotalDisplayRecords);

            var lists = queryPaginated.ToList();
            var records = from l in lists
                          select new DtoOptInList
                          {
                              ListID = l.ListID,
                              ListName = l.ListName,
                              PortalID = l.PortalID,
                              NewsLetterCount = int.Parse(l.NewsLetterCount.ToString()),
                              SubscriberCount = int.Parse(l.SubscriberCount.ToString())
                          };
            dtoLists.AddRange(records);
            return dtoLists;
        }
        #endregion

        #region TrackingLink Methods

        public OptInLinkTracking GetTrackingLink(int linkTrackingID)
        {
            return _repository.GetTrackingLink(linkTrackingID);
        }
        public bool EditTrackingLink(OptInLinkTracking trackingLinkToEdit)
        {
            // Database logic
            try
            {
                _repository.EditTrackingLink(trackingLinkToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }
        public bool CreateTrackingLink(OptInLinkTracking linkToCreate)
        {
            try
            {
                _repository.CreateTrackingLink(linkToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public IEnumerable<OptInLinkTracking> ListTrackingLinks(int newsLetterId)
        {
            return _repository.ListTrackingLinks(newsLetterId);
        }

        public bool DeleteTrackingLink(int listId)
        {
            try
            {
                _repository.DeleteTrackingLink(listId);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        #endregion

        #region NewsLetter Methods
        private string GetFooter(int newsLetterId, int subscriberId)
        {
            var siteURL = _cacheService.GetCurrentPortal().URLAuthority;
            string footerHTML = "<div align='center'>";
            footerHTML += "<a href='http://" + siteURL + "/optin-manager/subscription/" + subscriberId + "'>Unsubscribe</a> | ";
            footerHTML += "<a href='http://" + siteURL + "/optin-manager/subscription/" + subscriberId + "'>Settings</a>";
            footerHTML += "<br />";
            footerHTML += "<a href='http://" + siteURL + "'>";
            footerHTML += "<img border='0' src='http://" + siteURL + "/optin-manager/trackingimage/" + newsLetterId + "' alt='mvccms-optin-newsletter'>";
            footerHTML += "</a>";
            footerHTML += "</div>";
            return footerHTML;
        }
        public bool SendNewsLetter(int newsLetterId, int subscriberId)
        {
            try
            {
                var newsLetter = _repository.GetNewsLetter(newsLetterId);
                var footerHtml = GetFooter(newsLetterId, subscriberId);
                var subscriber = _repository.GetSubscriber(subscriberId);
                if (subscriber.DoNotContact) { return true; }

                MvcCmsEmails.NewsLetter(newsLetter, subscriber, footerHtml, _cacheService.GetCurrentPortal());
                if (newsLetter.Status != "Sent") {
                    newsLetter.Status = "Sent";
                    newsLetter.Sent = DateTime.Now;
                    _repository.EditNewsLetter(newsLetter);
                }
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool DeleteNewsLetter(int newsLetterId)
        {
            try
            {
                _repository.DeleteNewsLetter(newsLetterId);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public bool EditNewsLetter(OptInNewsLetter newsLetterToEdit)
        {
            // Database logic
            try
            {
                newsLetterToEdit.Status = "Edit";
                _repository.EditNewsLetter(newsLetterToEdit);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public OptInNewsLetter GetNewsLetter(int newsLetterId)
        {
            return _repository.GetNewsLetter(newsLetterId);
        }

        public bool CreateNewsLetter(OptInNewsLetter newsLetterToCreate)
        {
            newsLetterToCreate.PortalID = _cacheService.GetCurrentPortal().PortalID;
            try
            {
                newsLetterToCreate.Status = "Created";
                _repository.CreateNewsLetter(newsLetterToCreate);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
            return true;
        }

        public DtoOptInNewsLetters ListNewsLetters(DataTablesRequest dataTablesRequest, string columnsToReturn)
        {
            var columnNames = columnsToReturn.Split(',');
            var dtoNewsLetters = new DtoOptInNewsLetters();

            var query = _repository.ListNewsLetters(_cacheService.GetCurrentPortal().PortalID).AsQueryable();
            //Get the totalItems/TotalRecords now we need this before filters are applied
            dtoNewsLetters.GetPagination.iTotalRecords = query.Count();

            query = query.SearchForDataTables(dataTablesRequest, columnNames) as IQueryable<OptInNewsLetterWithCounts>;
            //Get the totalDisplayRecords after filtering has been applied.
            dtoNewsLetters.GetPagination.iTotalDisplayRecords = query.Count();

            query = DataTablesSort.Sort(dataTablesRequest, query, columnNames) as IQueryable<OptInNewsLetterWithCounts>;

            //Pass the number of records after filtering to AsPagination so it doesn't query again for the total
            //The filtered count is the one it is expecting to calculate TotalPages
            var queryPaginated = query.AsPagination(dataTablesRequest.iCurrentPage, dataTablesRequest.iDisplayLength, dtoNewsLetters.GetPagination.iTotalDisplayRecords);

            var newsLetters = queryPaginated.ToList();
            var records = from n in newsLetters
                          select new DtoOptInNewsLetter
                          {
                              NewsLetterID = n.NewsLetterID,
                              Name = n.Name,
                              PortalID = n.PortalID,
                              Recipients = int.Parse(n.Recipients.ToString()),
                              Status = n.Status,
                              ReleaseDate = n.ReleaseDate
                          };
            dtoNewsLetters.AddRange(records);
            return dtoNewsLetters;
        }

        public bool AddListToNewsLetter(int newsLetterId, int listId)
        {
            try
            {
                var newsLetter = GetNewsLetter(newsLetterId);
                var list = GetList(listId);
                newsLetter.Lists.Add(list);
                return EditNewsLetter(newsLetter);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
        }
        public bool RemoveListFromNewsLetter(int newsLetterId, int listId)
        {
            try
            {
                var newsLetter = GetNewsLetter(newsLetterId);
                var list = GetList(listId);
                newsLetter.Lists.Remove(list);
                return EditNewsLetter(newsLetter);
            }
            catch (Exception ex)
            {
                AppLogging.GetLogger().Error(GeneralMethods.GetStackNames(), ex);
                return false;
            }
        }
        #endregion

    }
}